package me.henhaoji.wxtoken;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import me.henhaoji.tokenmgr.util.WeixinUtil;
import net.sf.json.JSONObject;
/**
 * Token管理器
 * 
 * @author Hagen
 * */

public class TokenService {


	private static TokenService instance=null;
	List<AccessToken> tokenList =null;
	Map<String,AccessToken> tokenMap = null;
	private BaseDao<AccessToken> dao = null;

	private long leftTime=900;
	private final String template = "https://api.weixin.qq.com/cgi-bin/token?"
			+ "grant_type=client_credential"
			+ "&appid=%s&secret=%s";
	private TokenTimer timer;
	private TokenService()
	{
		initMember();
		initData();
		//timer = new TokenTimer(this);
		//timer.start();
	}
	/**
	 * 初始化类成员
	 * */
	public void initMember()
	{
		tokenList = new ArrayList<AccessToken>();
		tokenMap = new HashMap<String,AccessToken>();
		dao = new BaseDao<AccessToken>();
	}
	/**
	 * 初始化计时器（当Listner开启时由Listener注入 默认不用）
	 * */
	public void initTimer()
	{
		if(timer==null)
		{
			timer = new TokenTimer(this);
			timer.start();
		}
	}
	/**
	 * 初始化数据，注意初始化数据时关Timer
	 * */
	public void initData()
	{
		tokenList  = dao.listHql("select atoken from AccessToken atoken");
		toMap();
	}

	public void toMap()
	{
		for(AccessToken token:tokenList)
		{
			tokenMap.put(token.getUserId(), token);
		}
	}
	/**
	 * 刷新全部，对超时的进行刷新
	 * */
	public void refreshAll()
	{
		int i=0;
		for(;i<tokenList.size();i++)
		{
			AccessToken token = tokenList.get(i);
			if(token==null)continue;
			long nows = System.currentTimeMillis()/1000;
			long time = token.getUpdateTime()+token.getExpires_in();
			long left = time-nows;
			if(left<leftTime)
			{
				 AccessToken at = get(token.getUserId());
				 int deep=0;
				 while((at==null||at.getId()==null||at.getErrcode()!=0)&&deep<3)
				 {
					 at=get(token.getUserId());
					 deep++;
				 }
				 if(at!=null&&at.getId()!=null&&at.getErrcode()==0)
				 {
					 tokenList.set(i, at);
				 }
			}
		}
		boolean valid=false;
		if(timer!=null){
			valid=timer.valid;
			timer.valid=false;
		}
		initData();
		if(timer!=null)timer.setValid(valid);
	}
	/**
	 * 强制从微信服务器刷新全部，慎用
	 * */
	public void refreshAllByForce()
	{
		int i=0;
		for(;i<tokenList.size();i++)
		{

				 AccessToken token = tokenList.get(i);
				 AccessToken at = getFromWeixin(token);
				 int deep=0;
				 while((at==null||at.getId()==null||at.getErrcode()!=0)&&deep<3)
				 {
					 at = getFromWeixin(token);
					 deep++;
				 }
				 if(at!=null&&at.getId()!=null&&at.getErrcode()==0)
				 {
					 tokenList.set(i, at);
					 tokenMap.put(at.getUserId(), at);
				 }
		}
		boolean valid = false;
		
		if(timer!=null){
			valid=timer.valid;
			timer.valid=false;
		}
		initData();
		if(timer!=null)timer.setValid(valid);
	}
	/**
	 * 刷新一个
	 * 
	 * @param appId
	 * */
	public void refreshOne(String appId)
	{
		int i=0;
		for(;i<tokenList.size();i++)
		{
			AccessToken token = tokenList.get(i);
			if(token!=null&&token.getId()!=null&&token.getId().equals(appId))
			{
				AccessToken at = get(token.getUserId());
				 int deep=0;
				 while((at==null||at.getId()==null||at.getErrcode()!=0)&&deep<3)
				 {
					 at = get(token.getUserId());
					 deep++;
				 }
				if(at!=null&&at.getId()!=null&&at.getErrcode()==0){
					tokenList.set(i, at);
					tokenMap.put(at.getUserId(), at);
					break;
				}
			}
			
		}
	}
	/**
	 * 从微信服务器上刷新一个
	 * @param appId
	 * */
	public void refreshOneByForce(String appId)
	{
		int i=0;
		for(;i<tokenList.size();i++)
		{
			AccessToken token = tokenList.get(i);
			if(token!=null&&token.getId()!=null&&token.getId().equals(appId))
			{
				AccessToken at = getFromWeixin(token);
				int deep=0;
				while((at==null||at.getId()==null||at.getErrcode()!=0)&&deep<3)
				{
					at = getFromWeixin(token);
					deep++;
				}
				if(at!=null&&at.getId()!=null&&at.getErrcode()==0){
					tokenList.set(i, at);
					tokenMap.put(at.getUserId(), at);
					break;
				}
			}
			
		}
	}
	/**
	 * 从微信服务器上刷新一个
	 * @param userId
	 * */
	public void getOneByForce(String userId)
	{
		int i=0;
		for(;i<tokenList.size();i++)
		{
			AccessToken token = tokenList.get(i);
			if(token!=null&&token.getUserId()!=null&&token.getUserId().equals(userId))
			{
				AccessToken at = getFromWeixin(token);
				int deep=0;
				while((at==null||at.getId()==null||at.getErrcode()!=0)&&deep<3)
				{
					at = getFromWeixin(token);
					deep++;
				}
				if(at!=null&&at.getId()!=null&&at.getErrcode()==0){
					tokenList.set(i, at);
					tokenMap.put(at.getUserId(), at);
					break;
				}
			}
			
		}
	}
	/**
	 * 刷新一个
	 * @param userId
	 * */
	public AccessToken get(String userId)
	{
		AccessToken token = tokenMap.get(userId);
		if(token!=null)
		{
			 long nows = System.currentTimeMillis()/1000;
			 long time = token.getUpdateTime()+token.getExpires_in();
			 long left = time-nows;
			 if(left<leftTime)
			 {
				 Logger.getLogger(TokenService.class).log(Level.INFO, token.getId());
				AccessToken at = getFromWeixin(token);
				int deep=0;
				while((at==null||at.getId()==null||at.getErrcode()!=0)&&deep<3)
				{
					at = getFromWeixin(token);
					deep++;
				}
				if(at!=null&&at.getErrcode()==0)
				{
					tokenMap.put(at.getUserId(), at);
					int i=0;
					for(;i<tokenList.size();i++)
					{
						AccessToken ats = tokenList.get(i);
						if(ats.getId().equals(at.getId()))
						{
							tokenList.set(i, at);
							break;
						}
					}
					return at;
				}
				else
				{
					return null;
				}
			 }
			 else
			 {
				 return token;
			 }
		}
		else
		{
			return null;
		}
	}
	/**
	 * 从微信获取数据
	 * 
	 * @param token 需包含appId 和 appSecret
	 * 
	 * @return AccessToken 当token存在且id不为空、errcode为0时有效
	 * */
	private AccessToken getFromWeixin(AccessToken token)
	{
		 String realUrl = String.format(template, token.getId(),token.getSecret());
		 Logger.getLogger(TokenService.class).log(Level.INFO, realUrl);
		 AccessToken at= new AccessToken();
		 String res=null;
			try{
				res=WeixinUtil.httpsRequest(realUrl, "GET",null);
				Logger.getLogger(TokenService.class).log(Level.INFO,res);
			}catch(Exception e)
			{
				e.printStackTrace();
			}
			if(res!=null)
			{
				JSONObject jsonObject = JSONObject.fromObject(res);
				at = (AccessToken) JSONObject.toBean(jsonObject, AccessToken.class);
				if(at.getErrcode()==-1)
				{
					try {
						Thread.sleep(500);
						//get();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}else if(at.getErrcode()==0)
				{
					//doSomething
					long lastGetTime=System.currentTimeMillis()/1000;
					at.setUpdateTime(lastGetTime);
					at.setId(token.getId());
					at.setSecret(token.getSecret());
					at.setUserId(token.getUserId());
					at.setRemark(token.getRemark());
					
					dao.createOrUpdate(at);
					return at;
				}
				else
					return at;
				//System.out.println(at.getAccess_token());
				//System.out.println(at.getExpires_in());
				
			}
			return null;
		 
	}

	public long getLeftTime() {
		return leftTime;
	}

	public void setLeftTime(long leftTime) {
		this.leftTime = leftTime;
	}
	
	public List<AccessToken> getTokenList() {
		return tokenList;
	}

	public void setTokenList(List<AccessToken> tokenList) {
		this.tokenList = tokenList;
	}

	public Map<String, AccessToken> getTokenMap() {
		return tokenMap;
	}

	public void setTokenMap(Map<String, AccessToken> tokenMap) {
		this.tokenMap = tokenMap;
	}
	
	
/**
 * 单例
 * */
	public static TokenService getInstance()
	{
		if(instance==null)
		{
			synchronized(TokenService.class)
			{
				if(instance==null)
				{
					instance = new TokenService();
				}
			}
		}
		return instance;
	}
//TODO add token delete token //close timer->service->start timer
	/**
	 * 向数据库中添加一条token 需要包含id、secret、userId等信息
	 * */
	public boolean add(AccessToken token)
	{
		boolean res=false;
		AccessToken at = getFromWeixin(token);
		if(at!=null&&at.getAccess_token()!=null&&at.getErrcode()==0)
		{
			
			res=dao.createOrUpdate(at);
			
			boolean valid=false;
			if(timer!=null){
				valid=timer.valid;
				timer.valid=false;
			}
			initData();
			if(timer!=null)timer.setValid(valid);
		}
		return res;
	}
	/**
	 * 从数据库删除一条数据
	 * */
	public boolean delete(AccessToken token)
	{
		boolean res=false;
		if(token!=null)
		{
			boolean valid=false;
			if(timer!=null)
			{
				valid=timer.valid;
				stopTimer();
			}
			 res = dao.delete(token);
			if(dao.find(AccessToken.class, token.getId())==null)
			{
				res=true;
			}
			else
			{
				res=dao.delete(token);
			}
			initData();
			timer.setValid(valid);
		}
		return res;
	}
	/**
	 * 使定时器开启刷新功能 默认为空定时器
	 * */
	public boolean runTimer()
	{
		if(timer!=null)
		{
			timer.setValid(true);
			return true;
		}
		return false;
	}
	/**
	 * 使定时器关闭刷新功能
	 * */
	public boolean stopTimer()
	{
		if(timer!=null)
		{
			timer.setValid(false);
			return true;
		}
		return false;
	}
	/**
	 * 为Listner注入提供接口
	 * */
	void setTimer(TokenTimer timer)
	{
		this.timer = timer;
	}
	public TokenTimer getTimer()
	{
		return timer;
	}
}
